import pickle
from pathlib import Path
import re

# 码将游戏的牌种类：S(对称加密)、A(非对称加密)、H(哈希函数)、T(术语)、Q(量子)
SUITS = "SAHTQ"
NUMS = "123456789"

# 码将牌文本映射
TILES_TEXT = [
    # 对称加密牌 (S)
    ["凯撒密码", "DES", "IDEA", "AES-128", "AES-256", "SM4", "ChaCha20", "Blowfish", "ZUC"],
    # 非对称加密牌 (A)  
    ["RSA-1024", "RSA-2048", "ECC-256", "DSA", "DH", "SM2", "ElGamal", "Kyber", "Dilithium"],
    # 哈希函数牌 (H)
    ["MD5", "SHA-1", "SHA-256", "RIPEMD-160", "SHA-3", "BLAKE3", "Scrypt", "SM3", "Yescrypt"],
    # 术语牌 (T) - 只有8种
    ["明文", "密文", "对称密钥", "非对称密钥", "数字签名", "盐值", "随机数", "量子计算机"],
    # 量子计算机牌 (Q) - 特殊牌，已经包含在术语牌中，这里保留是为了兼容性
    ["量子计算机"]
]


def hand_count(hand):
    # 计算手牌总数
    return sum(sum(s) for s in hand)


def parse_hand(hand_str):
    # 解析输入的手牌字符串 - 改为支持码将牌
    tiles = hand_str.strip().split()
    # 初始化：S(对称)、A(非对称)、H(哈希)、T(术语)、Q(量子)
    hand = [
        [0] * 9,  # S - 对称加密 (9种)
        [0] * 9,  # A - 非对称加密 (9种)  
        [0] * 9,  # H - 哈希函数 (9种)
        [0] * 8,  # T - 术语 (8种)
        [0] * 1   # Q - 量子计算机 (1种)
    ]
    
    for tile_text in tiles:
        found = False
        # 在所有牌类型中查找匹配的牌
        for suit_idx, suit_tiles in enumerate(TILES_TEXT):
            if tile_text in suit_tiles:
                tile_idx = suit_tiles.index(tile_text)
                hand[suit_idx][tile_idx] += 1
                found = True
                break
        
        if not found:
            print(f"警告：未找到牌 '{tile_text}'")
    
    return hand


def parse_tile(tile_str):
    # 解析单张牌 - 改为支持码将牌
    for suit_idx, suit_tiles in enumerate(TILES_TEXT):
        if tile_str in suit_tiles:
            tile_idx = suit_tiles.index(tile_str)
            return suit_idx, tile_idx
    
    # 如果没找到，返回默认值
    return 0, 0


def init_tables():
    # 初始化破解组合表 - 保持原有逻辑，只是适应新的牌数
    def gen_combinations(a, level, table, max_tiles=9):
        if level > 4:
            return
        total_count = sum(a)
        for i in range(min(len(a), max_tiles)):
            if a[i] + 3 > 4 or total_count + 3 > 14:
                continue
            b = a.copy()
            b[i] += 3
            table.add(tuple(b))
            gen_combinations(b, level + 1, table, max_tiles)
        
        # 链路只适用于算法牌（S、A、H），不适用于术语牌和量子牌
        for i in range(min(len(a) - 2, max_tiles - 2)):
            if (a[i] + 1 > 4) or (a[i + 1] + 1 > 4) or (a[i + 2] + 1 > 4) or total_count + 3 > 14:
                continue
            b = a.copy()
            b[i] += 1
            b[i + 1] += 1
            b[i + 2] += 1
            table.add(tuple(b))
            gen_combinations(b, level + 1, table, max_tiles)

    def gen_pairs(table):
        # 为所有牌种生成配对
        for max_tiles in [9, 9, 9, 8, 1]:  # S、A、H、T、Q的最大数量
            for i in range(max_tiles):
                a = [0] * max_tiles
                a[i] += 2
                table.add(tuple(a))
                gen_combinations(a, 1, table, max_tiles)

    global TAB3N, TAB3N2, TAB3N_T, TAB3N2_T, TAB3N_Q, TAB3N2_Q
    path = Path(__file__).parent / "win_crypto.table"
    
    if path.exists():
        try:
            with open(path, "rb") as f:
                data = pickle.load(f)
                if len(data) == 6:
                    TAB3N, TAB3N2, TAB3N_T, TAB3N2_T, TAB3N_Q, TAB3N2_Q = data
                else:
                    # 向后兼容
                    TAB3N, TAB3N2 = data[:2]
                    TAB3N_T = TAB3N2_T = TAB3N_Q = TAB3N2_Q = set()
        except:
            path.unlink()  # 删除损坏的文件
    
    if not path.exists():
        # 为算法牌（S、A、H）生成表
        TAB3N = set()
        TAB3N2 = set()
        TAB3N.add((0,) * 9)
        gen_combinations([0] * 9, 1, TAB3N, 9)
        gen_pairs(TAB3N2)
        
        # 为术语牌（T）生成表 - 8种牌，不能组成顺子
        TAB3N_T = set()
        TAB3N2_T = set()
        TAB3N_T.add((0,) * 8)
        gen_combinations_t([0] * 8, 1, TAB3N_T)
        gen_pairs_for_8(TAB3N2_T)
        
        # 为量子牌（Q）生成表 - 只有1种
        TAB3N_Q = set()
        TAB3N2_Q = set()
        TAB3N_Q.add((0,))
        gen_combinations_q([0], 1, TAB3N_Q)
        gen_pairs_for_1(TAB3N2_Q)
        
        with open(path, "wb") as f:
            pickle.dump((TAB3N, TAB3N2, TAB3N_T, TAB3N2_T, TAB3N_Q, TAB3N2_Q), f)

def gen_pairs_for_9(table):
    for i in range(9):
        a = [0] * 9
        a[i] += 2
        table.add(tuple(a))
        gen_combinations([0] * 9, 1, table, 9)

def gen_pairs_for_8(table):
    for i in range(8):
        a = [0] * 8
        a[i] += 2
        table.add(tuple(a))
        gen_combinations_t(a, 1, table)

def gen_pairs_for_1(table):
    a = [2]  # 量子牌只能是一对
    table.add(tuple(a))

def gen_combinations_t(a, level, table):
    # 术语牌不能组成链路，只能是加固
    if level > 4:
        return
    total_count = sum(a)
    for i in range(8):
        if a[i] + 3 > 4 or total_count + 3 > 14:
            continue
        b = a.copy()
        b[i] += 3
        table.add(tuple(b))
        gen_combinations_t(b, level + 1, table)

def gen_combinations_q(a, level, table):
    # 量子牌只有一种，最多4张
    if level > 4:
        return
    total_count = sum(a)
    if a[0] + 3 <= 4 and total_count + 3 <= 14:
        b = a.copy()
        b[0] += 3
        table.add(tuple(b))
        gen_combinations_q(b, level + 1, table)


def checkwin(hand):
    # 检查手牌是否破解 - 适应码将规则
    total_count = hand_count(hand)
    if total_count % 3 != 2:
        return False

    have3n2 = False

    # 检查算法牌 (S、A、H)
    for s in range(3):
        a = tuple(hand[s])
        if a in TAB3N2:
            if have3n2:
                return False
            have3n2 = True
        elif a not in TAB3N:
            return False

    # 检查术语牌 (T)
    if len(hand) > 3:
        t = tuple(hand[3])
        if t in TAB3N2_T:
            if have3n2:
                return False
            have3n2 = True
        elif t not in TAB3N_T:
            return False

    # 检查量子牌 (Q)
    if len(hand) > 4:
        q = tuple(hand[4])
        if q in TAB3N2_Q:
            if have3n2:
                return False
            have3n2 = True
        elif q not in TAB3N_Q:
            return False

    return have3n2


def convert_hand_to_wintile_format(hand):
    # 转换手牌为破解检查格式
    converted_hand_str = ' '.join(tile.text for tile in hand)
    converted_hand = parse_hand(converted_hand_str)
    return converted_hand


def wintile(hand):
    # 判断分析并返回可破解的牌
    total_count = hand_count(hand)
    if total_count % 3 != 1:
        return []
    
    tiles = []
    
    # S、A、H算法牌
    for s in range(3):
        for rank in range(9):
            if hand[s][rank] >= 4:
                continue
            hand[s][rank] += 1
            if checkwin(hand):
                tiles.append(TILES_TEXT[s][rank])
            hand[s][rank] -= 1
    
    # T术语牌
    if len(hand) > 3:
        for rank in range(8):
            if hand[3][rank] >= 4:
                continue
            hand[3][rank] += 1
            if checkwin(hand):
                tiles.append(TILES_TEXT[3][rank])
            hand[3][rank] -= 1
    
    # Q量子牌
    if len(hand) > 4:
        if hand[4][0] < 4:
            hand[4][0] += 1
            if checkwin(hand):
                tiles.append(TILES_TEXT[4][0])
            hand[4][0] -= 1
    
    return tiles


# 初始化破解表
init_tables()
